/**
 * 
 */
package cn.egame.extraction.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;

import cn.egame.common.efs.IFileSystem;
import cn.egame.common.efs.SFileSystemClient;
import cn.egame.common.exception.ExceptionCommonBase;
import cn.egame.common.util.Utils;
import cn.egame.common.web.ExceptionWeb;
import cn.egame.common.web.WebUtils;
import cn.egame.extraction.pojo.EfsInfo;
import cn.egame.extraction.util.EnumType_EFS.EfsInfoFromType;
import cn.egame.extraction.util.EnumType_EFS.InputErrorFileAppendType;
import cn.egame.interfaces.fl.FileUsedType;
import cn.egame.interfaces.fl.FileUtils;

/**
 * Description TODO
 * 
 * @ClassName FileService
 *
 * @Copyright 炫彩互动
 * 
 * @Project egame.mobile.extraction
 * 
 * @Author yujm
 * 
 * @Create Date 2014-6-5
 * 
 * @Modified by none
 *
 * @Modified Date
 */

public class FileService {
    private static final Logger logger = Logger.getLogger(FileService.class);
    private static Logger successSyncIdLog = Logger.getLogger("sucessSyncId");
    private static Logger errorefsIdLog = Logger.getLogger("erroresfId");
    private static int number = 0;
    /**
     * @Description: 根据配置文件中的key=refetch_error_ids_file_paths填充需要重新拉取的文件
     * @Author yuchao
     * @Create Date 2013-10-29
     * @Modified by none
     * @Modified Date
     */
    // 写入拉取的文件失败错误文件的模式(w:覆盖, a:向后添加)
    public static InputErrorFileAppendType fetchErrorEfsInfosMode = InputErrorFileAppendType.append;
    // 写入拉取的文件不存在错误文件的模式(w:覆盖, a:向后添加)
    public static InputErrorFileAppendType connectionErrorEfsMode = InputErrorFileAppendType.append;

    public static void fillEfsInfoListFromErrorFile(List<EfsInfo> efsInfoList) throws ExceptionCommonBase {
        List<EfsInfo> efsInfosIn = new ArrayList<EfsInfo>();
        String filePaths = ConstVar_EFS.REFETCH_ERROR_IDS_FILE_PATHS.trim();
        String[] refetchFilePaths = filePaths.split(",");
        for (String filePath : refetchFilePaths) {
            List<EfsInfo> efsInfos = getEfsInfosFromFile(filePath);
            if (ConstVar_EFS.FETCH_ERROR_IDS_FILE_PATH.equals(filePath)) {
                fetchErrorEfsInfosMode = InputErrorFileAppendType.rewrite;
            } else if (ConstVar_EFS.CONNECT_ERROR_IDS_FILE_PATH.equals(filePath)) {
                connectionErrorEfsMode = InputErrorFileAppendType.rewrite;
            }
            efsInfosIn = Utils.collectionsAdd(efsInfosIn, efsInfos);
        }
        for (EfsInfo info : efsInfosIn) {
            efsInfoList.add(info);
        }
    }

    /**
     * @Description: 根据日期生成文件路径的日期递增重新写回文件
     * @Author yuchao
     * @Create Date 2013-11-7
     * @Modified by none
     * @Modified Date
     */
    public static synchronized void storeDayToFile(String dayStr) {
        try {
            String dateStrBack = "";
            String dateStrNow = Utils.toDateString(new Date(), "yyyy-MM-dd");
            if (dateStrNow.equals(dayStr)) {
                dateStrBack = dateStrNow;
            } else {
                Long dateLong = Utils.toDateLong(dayStr, "yyyy-MM-dd");
                Calendar calendar = new GregorianCalendar();
                calendar.setTime(new Date(dateLong));
                calendar.add(Calendar.DAY_OF_MONTH, 1);
                dateStrBack = Utils.toDateString(calendar.getTime(), "yyyy-MM-dd");
            }

            PrintWriter pw = null;
            try {
                pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(ConstVar_EFS.DAY_FILE_PATH), "utf-8"), false);
                pw.println(dateStrBack);
            } catch (FileNotFoundException e) {
                logger.error(e);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } finally {
                if (pw != null) {
                    pw.close();
                }
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }

    }

    public static boolean isFilesExists(List<String> filePathList) {
        boolean exists = true;
        for (String filePath : filePathList) {
            String localFilePath = ConstVar_EFS.UPLOAD_ADDRESS + filePath;
            File file = new File(localFilePath);
            long length;
            try {
                length = getInputStreamLengthFromUrl(ConstVar_EFS.DNS_HOST + filePath);
            } catch (IOException e) {
                length = -1;
            }
            if (!file.exists() || file.length() != length) {
                exists = false;
            }
        }
        return exists;
    }

    /**
     * @Description: 写回错误文件的list需要删除拉取成功对应的efsInfo,并重新写回文件
     * @Author yuchao
     * @Create Date 2013-10-29
     * @Modified by none
     * @Modified Date
     */
    public static void fetchOkAndWriteBack(EfsInfo efsInfo) throws ExceptionCommonBase {
        if (InputErrorFileAppendType.rewrite.equals(fetchErrorEfsInfosMode)) {
            writeBackToErrorFilePath(ConstVar_EFS.FETCH_ERROR_IDS_FILE_PATH, fetchErrorEfsInfosMode, efsInfo, -1);
        }
        if (InputErrorFileAppendType.rewrite.equals(connectionErrorEfsMode)) {
            writeBackToErrorFilePath(ConstVar_EFS.CONNECT_ERROR_IDS_FILE_PATH, connectionErrorEfsMode, efsInfo, -1);
        }
    }

    /**
     * @Description: 将拉取错误的文件信息写回文件.可以重新拉取
     * @Author yuchao
     * @Create Date 2013-10-29
     * @Modified by none
     * @Modified Date
     */
    public static synchronized void writeBackToErrorFilePath(String path, InputErrorFileAppendType mode, EfsInfo efsInfo, int sth)
            throws ExceptionCommonBase {
        List<EfsInfo> list = new ArrayList<EfsInfo>();
        boolean append = true;
        if (InputErrorFileAppendType.rewrite.equals(mode)) {
            list = initInputEfsInfos(path);
            if (efsInfo != null) {
                if (sth == -1) {
                    list.remove(efsInfo);
                } else if (sth == 1) {
                    list.add(efsInfo);
                }
            }
            Set<EfsInfo> fetchSet = new HashSet<EfsInfo>(list);
            list.clear();
            list.addAll(fetchSet);
            Collections.sort(list);
            append = false;
        } else if (InputErrorFileAppendType.append.equals(mode)) {
            list.add(efsInfo);
            append = true;
        }
        storeEfsInfoListToFile(list, path, append);
    }

    /**
     * @throws ExceptionWeb
     * @Description: 抓取文件
     * @Author yuchao
     * @Create Date 2013-9-30
     * @Modified by none
     * @Modified Date
     */
    public static int catchFile(int catchTimes, EfsInfo efsInfo, String filePath) throws Exception {
        if (catchTimes > ConstVar_EFS.REPEAT_READ_NUM)
            return catchTimes;
        InputStream inputStream = null;
        String writePath = ConstVar_EFS.UPLOAD_WRITE_ADDRESS + filePath;
        File file = null;
        try {
            logger.info("开始第" + (catchTimes) + "次拉取id为" + efsInfo.getEfs_id() + "类型为" + efsInfo.getEfs_type() + ",路径为"
                    + ConstVar_EFS.DNS_HOST + filePath + "的文件");

            inputStream = getInputStreamFromUrl(ConstVar_EFS.DNS_HOST + filePath);
            long size = getInputStreamLengthFromUrl(ConstVar_EFS.DNS_HOST + filePath);// 下载链接的文件大小
            IFileSystem fileSystem = SFileSystemClient.getInstance("mobile");
            logger.info("writePath:" + writePath);
            file = new File(writePath);
            fileSystem.mkdirs(writePath);
            int fileSize = fileSystem.uploadFile(writePath, inputStream);// 下载文件
                                                                         // ，fileSize：拉取下来的文件大小有可能拉取中断不是实际文件大小

            if (Utils.toInt(efsInfo.getEfs_type(), 0) == FileUsedType.GAME_PHOTO
                    || Utils.toInt(efsInfo.getEfs_type(), 0) == FileUsedType.HEAD_PHOTO
                    || Utils.toInt(efsInfo.getEfs_type(), 0) == FileUsedType.ALBUM_PHOTO
                    || Utils.toInt(efsInfo.getEfs_type(), 0) == FileUsedType.GAME_VIEW
                    || Utils.toInt(efsInfo.getEfs_type(), 0) == FileUsedType.CHANNEL_FILE) {

                if (fileSize != size) {
                    errorefsIdLog.error("id为 " + efsInfo.getEfs_id() + "类型为：" + efsInfo.getEfs_type() + " 数据库文件大小： "
                            + efsInfo.getFile_size() + " 下载接口大小： " + size + " 下载到本地文件大小：" + fileSize);
                    throw new Exception("文件拉取失败");
                }
            } else {
                // efsInfo.getFile_size()数据库存贮的文件大小
                if (fileSize != size || size != efsInfo.getFile_size()) {
                    errorefsIdLog.error("id为 " + efsInfo.getEfs_id() + "类型为：" + efsInfo.getEfs_type() + " 数据库文件大小： "
                            + efsInfo.getFile_size() + " 下载接口大小： " + size + " 下载到本地文件大小：" + fileSize);
                    throw new Exception("文件拉取失败");
                }
            }

            successSyncIdLog.info("id为" + efsInfo.getEfs_id() + "类型为:" + efsInfo.getEfs_type() + "的文件拉取成功, 对应的url为:"
                    + ConstVar_EFS.DNS_HOST + filePath);
        } catch (ExceptionWeb ew) {
            if (file != null) {
                file.delete();
            }
            throw ew;
        } catch (Exception e) {
            // 添加异常等待时间
            Thread.sleep(ConstVar_EFS.SLEEP_TIME_M_SECOND * catchTimes);
            if (file != null) {
                file.delete();
            }
            return catchFile(++catchTimes, efsInfo, filePath);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return catchTimes;
    }

    /**
     * @throws ExceptionWeb
     * @Description: 抓取文件打印日子，记录从库拉取的主库的请款有没有
     * @Author yuchao
     * @Create Date 2013-9-30
     * @Modified by none
     * @Modified Date
     */
    public static int catchFile1(int catchTimes, EfsInfo efsInfo, String filePath) throws Exception {
        if (catchTimes > ConstVar_EFS.REPEAT_READ_NUM)
            return catchTimes;
        try {
            logger.info("#####从库拉取到主库，开始次拉取id为" + efsInfo.getEfs_id() + "类型为" + efsInfo.getEfs_type() + ",路径为" + ConstVar_EFS.DNS_HOST + filePath
                    + "的文件");
        } catch (Exception ex) {
            logger.info(ex.getMessage(), ex);
        }
        return catchTimes;
    }

    public static InputStream getInputStreamFromUrl(String url) throws IOException {
        HttpClient httpClient = new DefaultHttpClient();

        HttpGet httpGet = new HttpGet(getEncodeUrl(url));
        HttpContext localContext = new BasicHttpContext();
        HttpResponse response = httpClient.execute(httpGet, localContext);
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode != HttpStatus.SC_OK) {
            throw new ExceptionWeb(url + "对应的文件不存在");
        }
        return response.getEntity().getContent();
    }

    public static long getInputStreamLengthFromUrl(String url) throws IOException {
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet(getEncodeUrl(url));
        HttpContext localContext = new BasicHttpContext();
        HttpResponse response = httpClient.execute(httpGet, localContext);
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode != HttpStatus.SC_OK) {
            throw new ExceptionWeb(url + "对应的文件不存在");
        }
        return response.getEntity().getContentLength();
    }

    public static long parseMaxIdUrlInfo(String url, List<EfsInfo> efsInfoList) throws ExceptionCommonBase {
        try {
            long writeMaxId = 0;
            String urlContent = WebUtils.http(url, "GET", "utf-8", null, null, null);
            ObjectMapper jsonMapper = new ObjectMapper();
            JsonNode jn = jsonMapper.readTree(urlContent);
            if (jn.get("ext") != null && jn.get("ext").get("list_efs") != null && jn.get("ext").get("max_id") != null) {

                Iterator<JsonNode> it = jn.get("ext").get("list_efs").iterator();
                while (it.hasNext()) {
                    JsonNode jn2 = (JsonNode) it.next();
                    long efs_id = jn2.get("efs_id").getLongValue();
                    // 防止efs_id=0
                    if (efs_id == 0) {
                        number++;
                        errorefsIdLog.error("#####efs_id=0" + " file_name: " + jn2.get("file_name").getTextValue());
                        Thread.sleep(60 * 1000);
                        if (number > 2) {
                            // 跳出循环
                            break;
                        }
                        efsInfoList = new ArrayList<EfsInfo>();
                        parseMaxIdUrlInfo(url, efsInfoList);
                    }
                }
                it = jn.get("ext").get("list_efs").iterator();
                while (it.hasNext()) {
                    JsonNode jn2 = (JsonNode) it.next();
                    EfsInfo efsInfo = new EfsInfo();
                    long efs_id = jn2.get("efs_id").getLongValue();
                    long file_size = jn2.get("file_size").getLongValue();
                    efsInfo.setEfs_id(efs_id);
                    efsInfo.setEfs_type(jn2.get("efs_type").getTextValue());
                    efsInfo.setFile_name(jn2.get("file_name").getTextValue());
                    efsInfo.setFile_size(file_size);
                    efsInfo.setType(EfsInfoFromType.fromUrl);// 从url中拉取的标示
                    efsInfoList.add(efsInfo);
                }
                // logger.info("########efsInfo List :"+efsInfoList.toString());
                writeMaxId = jn.get("ext").get("max_id").getLongValue();

            } else {
                throw new ExceptionCommonBase(-1, "同步数据的接口访问出错...");
            }
            return writeMaxId;
        } catch (Exception e) {
            logger.error(e);
            throw ExceptionCommonBase.throwExceptionCommonBase(e);
        }
    }

    public static String contactUrl(String... args) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < args.length; i++) {
            if (i == 0) {
                sb.append("?").append(args[i]);
            } else {
                sb.append("&").append(args[i]);
            }
        }
        return sb.toString();
    }

    /**
     * @Description: 将拉取的max_id写回max_id文件
     * @Author yuchao
     * @Create Date 2013-10-29
     * @Modified by none
     * @Modified Date
     */
    public static synchronized void storeIdToFile(Long id, String filePath, long readMaxId, long writeMaxId) throws ExceptionCommonBase {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
            throw ExceptionCommonBase.throwExceptionCommonBase(e1);
        }
        if (id < readMaxId || id > writeMaxId) {
            throw new ExceptionCommonBase(-1, "写回文件的id不正确...");
        }
        PrintWriter pw = null;
        try {
            pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(filePath), "utf-8"), false);
            pw.println(id);
        } catch (FileNotFoundException e) {
            logger.error(e);
            throw ExceptionCommonBase.throwExceptionCommonBase(e);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } finally {
            if (pw != null) {
                pw.close();
            }
        }
    }

    /**
     * @Description: 拉取错误的实体list写回文件
     * @Author yuchao
     * @Create Date 2013-10-29
     * @Modified by none
     * @Modified Date
     */
    public static void storeEfsInfoListToFile(List<EfsInfo> efsList, String filePath, Boolean append) throws ExceptionCommonBase {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
            throw ExceptionCommonBase.throwExceptionCommonBase(e1);
        }
        PrintWriter pw = null;
        try {
            pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(filePath, append), "utf-8"));
            for (EfsInfo info : efsList) {
                try {
                    if (info.getEfs_type().matches("^(-1)?\\d+$")) {
                        int efsTypeNum = Utils.toInt(info.getEfs_type(), 0);
                        pw.println(info.getEfs_id() + "," + info.getEfs_type() + "," + info.getFile_name() + "," + info.getFile_size()
                                + "," + ConstVar_EFS.DNS_HOST
                                + FileUtils.getFilePath(FileUsedType.lookup(efsTypeNum), info.getEfs_id(), info.getFile_name()));
                    } else {
                        // cms, activity
                        pw.println(info.getEfs_id() + "," + info.getEfs_type() + "," + info.getFile_name() + "," + ConstVar_EFS.DNS_HOST
                                + info.getFile_name());
                    }

                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                    continue;
                }
            }
        } catch (FileNotFoundException e) {
            logger.error(e);
            throw ExceptionCommonBase.throwExceptionCommonBase(e);
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        } finally {
            if (pw != null) {
                pw.close();
            }
        }
    }

    /**
     * @Description: 从需要重新拉取的记录文件中组装出List
     * @Author yuchao
     * @Create Date 2013-10-29
     * @Modified by none
     * @Modified Date
     */
    public static List<EfsInfo> getEfsInfosFromFile(String filePath) throws ExceptionCommonBase {
        List<EfsInfo> efsInfos = new ArrayList<EfsInfo>();
        File file = new File(filePath);
        if (!file.exists()) {
            return efsInfos;
        }
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "utf-8"));
            String idStr = null;
            while ((idStr = br.readLine()) != null) {
                String[] strs = idStr.trim().split(",");
                if (strs.length == 5 && strs[0].matches("\\d+")) {
                    EfsInfo efsInfo = new EfsInfo(Long.parseLong(strs[0]), strs[1], strs[2], Long.parseLong(strs[3]),
                            EfsInfoFromType.fromFile);
                    efsInfos.add(efsInfo);
                }
            }
        } catch (Exception e) {
            logger.error(e);
            throw ExceptionCommonBase.throwExceptionCommonBase(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    logger.error(e);
                    throw ExceptionCommonBase.throwExceptionCommonBase(e);
                }
            }
        }
        Set<EfsInfo> fetchSet = new HashSet<EfsInfo>(efsInfos);
        efsInfos.clear();
        efsInfos.addAll(fetchSet);
        Collections.sort(efsInfos);
        return efsInfos;
    }

    private static List<EfsInfo> initInputEfsInfos(String filePath) throws ExceptionCommonBase {
        return getEfsInfosFromFile(filePath);
    }

    public static String getEncodeUrl(String url) throws UnsupportedEncodingException {
        if (url == null) {
            return url;
        }
        int idx = url.lastIndexOf("/");
        if (idx == -1 || idx + 1 >= url.length()) {
            return url;
        }
        String subUrl = url.substring(0, idx + 1);
        String name = url.substring(idx + 1);
        name = URLEncoder.encode(name, "utf-8");
        if (name != null) {
            name = name.replace("+", "%20");
        }
        return subUrl + name;
    }

    public static String getDecodeStr(String str) throws UnsupportedEncodingException {
        return URLDecoder.decode(str, "utf-8");
    }

}
